Руководство по поиску и исправлению ошибок в проекте

Цель

При выполнении лабораторных работ вы непременно будете сталкиваться с множеством ошибок. И это нормально: "Не ошибается тот, кто ничего не делает" — © Джейсон Стейтем.
Важно воспитать в себе положительное восприятие обнаружения ошибок (ведь это приводит к улучшению вашего творения). Если относиться к обнаружению ошибок отрицательно, то вы подсознательно будете пытаться найти ошибки спустя рукава, но, если вы "в домике", и ошибок не видите — это не значит, что их нет.

При должном отношении, поиск ошибок может превратиться в увлекательное детективное расследование, где у вас есть "место преступления" (обнаруженное несоответствие в поведении, обычно это не сама ошибка, а ее следствие, круги на воде) и какой-то "набор улик" (фрагменты лога, исходный код). И вы по чуть-чуть будете разматывать "нераспутываемую паутину лжи", получая все новые улики, ведущие к истинной ошибке.

Этот документ посвящен практикуму по поискам подобных ошибок в SystemVerilog-коде.

Обратите внимание на то, как ставится ударение в словосочетании "временна́я диаграмма" (не "вре́менная"). В обиходе это словосочетание заменяется словом "времянка".

Алгоритм поиска ошибок

  1. Обычно всё начинается с сообщения в логе тестов (никто не проверяет глазами временную диаграмму сложных проектов, состоящую из тысяч сигналов, меняющихся миллионы раз за микросекунду), но на наших простых лабах, этот шаг иногда может быть и пропущен.
    Сообщение в логе обычно содержит следующую ключевую информацию: имя сигнала, на котором установилось неверное значение, и время когда это произошло. Чем лучше написаны тесты, тем больше ключевой информации будет отражено в сообщении, поэтому написание тестов является своего рода искусством.
  2. Получив имя сигнала и время, мы отправляемся на временную диаграмму и проверяем нашу ошибку. Как это сделать? Необходимо определить по коду, какие сигналы и каким образом управляют нашим сигналом. Вариантов может быть несколько:
    1. Управляющие сигналы имеют корректное значение, но логика, по которой они управляют сигналом неверна, из-за этого на нем возникает неверное значение. Это идеальный случай, при возникновении которого мы сразу же находим причину проблемы и исправляем ее.
    2. Логика управления верна, а какая-то часть управляющих сигналов имеет неверное значение (пусть для примера, неверное значение будет на управляющем сигнале X). Это означает, что обнаруженное несоответствие сигналов является уже следствием какой-то ошибки, и мы должны вернуться к шагу 2, проверяя источники сигналов для сигнала X. Так происходит до тех пор, пока мы не попадаем в тип 1.
    3. Логика управления и значения управляющих сигналов верны. Это самый сложный тип ошибок, который заключается либо в ошибке в спецификации разрабатываемого устройства, либо в САПРе или компонентах, влияющих на его работу. В рамках данного курса вас не должны заботить данные ошибки, и при их возникновении вам стоит обратиться к преподавателю (предварительно убедившись, что ошибка совершенно точно не подходит под первые два варианта).
    4. Любая возможная комбинация всех предыдущих типов.
  3. Обнаружив первопричину ошибки, мы исправляем ее (возможно дополняя набор тестов, или внеся правки в спецификацию), и повторно запускаем все тесты, чтобы убедиться в двух вещах:
    1. ошибка действительно исправлена
    2. исправление ошибки не породило новых ошибок

Давайте отработаем эти шаги на примере отладки ошибок в проекте по вычислению приблизительной длины вектора.

Работа с логом при появлении ошибок

После запуска симуляции мы видим в логе множество ошибок:

../.pic/Vivado%20Basics/Debug%20manual/fig_01.png

Рисунок 1. Пример сообщения об ошибках в тесте.

В любой ситуации с множеством ошибок, сначала надо разбираться с самой первой из них, поскольку она может быть причиной появления всех остальных. Поэтому листаем лог до момента первой ошибки:

../.pic/Vivado%20Basics/Debug%20manual/fig_02.png

Рисунок 2. Пример конкретной ошибки в тесте.

В логе сказано, что в момент времени 5ns, на дизайн подавались координаты вектора, равные 0 и 0, модель посчитала, что длина вектора равна нулю, в то время как дизайн вернул значение x.

Поиск ошибки на временной диаграмме

Давайте найдем это место на временной диаграмме. Обычно, сразу после запуска симуляции на временной диаграмме отображено место, где симуляция остановилась (возможно с очень неподходящим масштабом). Для начала подгоним масштаб таким образом, чтобы вся временная диаграмма умещалась в окне. Это делается либо нажатием правой кнопкой мыши по в области отображения сигналов, с выбором "Full View" во всплывающем меню, либо нажатием соответствующей кнопки на панели временной диаграммы (см. рис. 4), либо нажатием комбинации клавиш Ctrl+0. Затем найдем приблизительное место рядом с тем временем, что нас интересует, установим там курсор, и приблизим масштаб (покрутив колесиком мыши при зажатой клавише Ctrl), периодически уточняя местоположения курсора, пока не найдем интересующее нас место.

../.pic/Vivado%20Basics/Debug%20manual/fig_03.png

Рисунок 3. Пример временной диаграммы сразу поле остановки моделирования.

../.pic/Vivado%20Basics/Debug%20manual/fig_04.png

Рисунок 4. Пример установки масштаба временной диаграммы таким образом, чтобы та помещалась в текущем окне.

../.pic/Vivado%20Basics/Debug%20manual/fig_05.png

Рисунок 5. Пример временной диаграммы после подгонки масштаба.

../.pic/Vivado%20Basics/Debug%20manual/fig_06.png

Рисунок 6. Установка курсора в начало моделирования, чтобы, при увеличении масштаба, временная диаграмма сходилась к началу.

../.pic/Vivado%20Basics/Debug%20manual/fig_07.png

Рисунок 7. Временная диаграмма, отмасштабированная к времени ошибки с рис. 2.

Мы видим ровно ту информацию, которую нам предоставил тестбенч. Теперь надо разобраться в причинах возникновения X-состояния. Такое может произойти в двух ситуациях: какой-то из сигналов, формирующих этот находится в X или Z состоянии, либо же два каких-то сигнала одновременно пытаются выставить разные значения (подобный вариант встречается куда реже и в цикле ваших лабораторных вряд ли встретится).

Открытие файла исходного кода проблемного сигнала

В любом случае, первым делом необходимо определить, источник формирования значения сигнала res. Для этого, откроем файл с исходным кодом, где определен данный сигнал. Для этого, нажмем правой кнопкой мыши по имени сигнала на временной диаграмме, и выберем Go To Source Code:

../.pic/Vivado%20Basics/Debug%20manual/fig_08.png

Рисунок 8. Переход к месту объявления "проблемного" сигнала.

Открывается следующий код (с курсором на строчке wire [31:0] res;):

module tb();

reg [31:0] a;
reg [31:0] b;
wire [31:0] res;

vector_abs dut(
  .x(a),
  .y(b),
  .abs(res)
);
//...

Выделив res мы видим, что у нас подсветился res в строке abs(res). Это означает, что мы завели наш провод внутрь объекта dut модуля vector_abs, и у нас проблема второго типа (X-состояние передалось от выхода abs модуля vector_abs проводу res модуля tb).

В этом можно убедиться, если вытащить сигналы модуля vector_abs на временную диаграмму. Чтобы это сделать, надо переключиться на окно Scope, где размещена иерархия объектов нашего тестбенча

Добавление сигналов объектов на временную диаграмму

Обратите внимание, что в иерархии окна Scope находятся не имена модулей, а имена сущностей модуля. В приведенном выше листинге кода мы создали сущность модуля vector_abs с именем dut, поэтому в иерархии Scope мы видим внутри модуля верхнего уровня объект dut (не vector_abs), так будет и со всеми вложенными объектами.

Выделим объект dut. В окне Objects справа отобразятся все внутренние сигналы (входы/выходы, внутренние провода и регистры) объекта dut:

../.pic/Vivado%20Basics/Debug%20manual/fig_09.png

Рисунок 9. Отображение внутренних сигналов проверяемого модуля.

Вообще говоря, мы уже видим, что выход abs (к которому подключен наш провод res) находится в X-состоянии, но для отработки навыков, разберемся с добавлением на временную диаграмму. Можно поступить двумя способами:

  1. Добавить все сигналы (то, что видно в окне Objects на временную диаграмму) из окна Scope для этого, либо перетаскиваем нужный нам объект, зажав левую кнопку мыши на временную диаграмму, либо жмем правой кнопкой мыши по нужному объекту, и выбираем Add to Wave Window
  2. Добавить отдельные сигналы из окна Objects. Для этого выделяем их (возможно множественное выделение через модификаторы shift или ctrl), и как и в прошлом случае, либо перетаскиваем сигналы левой кнопкой мыши, либо добавляем их через правую кнопку мыши.

../.pic/Vivado%20Basics/Debug%20manual/fig_10.png

Рисунок 10. Добавление сигналов модуля на временную диаграмму.

../.pic/Vivado%20Basics/Debug%20manual/fig_11.png

Рисунок 11. Результат добавления сигналов модуля на временную диаграмму.

По мере роста сложности проекта, число сигналов на временной диаграмме будет постоянно расти, в связи с чем встает вопрос группировки сигналов.

Для того чтобы объединить сигналы в группу, необходимо их выделить. Это можно сделать двумя способами:

  1. "прокликав" интересующие сигналы при зажатой клавише Ctrl;
  2. если речь идет о диапазоне сигналов, можно выбрать сигнал с одного края, после чего, при зажатой клавише Shift, выбрать сигнал с другого края этого диапазона.

После выбора, необходимо нажать правой кнопкой мыши по выделенным сигналам, и в низу выпадающего списка выбрать New Group.

../.pic/Vivado%20Basics/Debug%20manual/fig_12.png

Рисунок 12. Пример создания группы сигналов (контекстное меню было обрезано для удобства отображения).

После создания группы, ей нужно будет дать имя. В случае, если все сигналы принадлежат одному модулю, удобно называть группу сигналов именем этого модуля.

../.pic/Vivado%20Basics/Debug%20manual/fig_13.png

Рисунок 13. Пример созданной группы сигналов.

Данну группу можно сворачивать и разворачивать, нажимая на соответствующую стрелку слева от имени группы.

Обратите внимание, что часть сигналов отображают какое-то значение (сигнал abs отображает X-состояние), а часть не отображают ничего. Так произошло, потому что провод abs непрерывно связан с проводом res, с точки зрения симулятора это одна сущность, и записывая во время моделирования значения для сигнала res, симулятор неявно записывал значения для сигнала abs, чего не скажешь про остальные сигналы, которых не было во время моделирования на временной диаграмме.

Сброс симуляции и ее повтор, установка времени моделирования

Для того, чтобы получить отсутствующие значения, необходимо повторить моделирование. Для этого, необходимо сбросить время моделирования в 0 и запустить его снова.

Для этого, необходимо на панели симуляции нажать кнопку Restart (|◀), а затем кнопку Run all () или Run for (▶t). Положение кнопок в окне Vivado иллюстрирует рис. 14.

../.pic/Vivado%20Basics/Debug%20manual/fig_14.png

Рисунок 14. Расположение кнопок, управляющих моделированием в окне Vivado.

Панель управления симуляции с кнопками:

  1. Restart, горячие клавиши: Ctrl+Shift+F5;
  2. Run all, горячая клавиша: F3;
  3. Run for, горячие клавиши: Shift+F2;
  4. Relaunch Simulation.

Run for выполняет моделирование указанного количества времени, после чего моделирование приостанавливается. Моделирование может быть остановлено так же и вручную, либо вызовом соответствующей инструкции из кода теста.

Run all отличается от Run for тем, что в качестве количества моделируемого времени указывается "бесконечность", и моделирование будет остановлено только вручную, либо вызовом соответствующей инструкции.

Обратите внимание, что для добавления недостающих значений добавленных сигналов лучше всего выполнять описанную выше инструкцию. Аналогичного результата можно добиться и нажатием на кнопку Relaunch Simulation, однако эта команда запускает повторную компиляцию и запуск симуляции, что для крупных проектов выльется в потерю времени на излишнюю компиляцию.

Кроме того, чтобы курсор и лог снова не ушли далеко от места первой ошибки, можно сразу указать, необходимое нам время моделирования перед выполнением команды Run for: 5ns.

../.pic/Vivado%20Basics/Debug%20manual/fig_15.png

Рисунок 15. Пример моделирования 5ns.

На рис. 16 представлен результат моделирования с новыми сигналами.

../.pic/Vivado%20Basics/Debug%20manual/fig_16.png

Рисунок 16. Результат повторного моделирования после добавления на временную диаграмму новых сигналов.

Видим два сигнала в Z-состоянии и один сигнал в X-состоянии. Обычно, сигналы с Z-состоянием проще всего исправить, т.к. зачастую это забытое или некорректное подключение провода. Кроме того, сигнал, зависящий от сигнала с Z-состоянием, может оказаться в X-состоянии, так что это может быть решением нашей проблемы, поэтому займемся проводами min и min_half. Сперва займемся сигналом min и перейдем к шагу 2 нашего алгоритма (нажимаем правой кнопкой мыши и выбираем Go To Source Code):

 module vector_abs(
   input [31:0] x,
   input [31:0] y,
   output[31:0] abs
 );


 wire [31:0] min;
 wire [31:0] min_half;

 max_min max_min_unit(
   .a(x),
   .b(y),
   .max(max),
   .min(min)
 );

Исправление сигналов с Z-состоянием

Мы видим, что сигнал min подключен к выходу min объекта max_min_unit модуля max_min. Добавим сигналы этого модуля на временную диаграмму. Для этого, необходимо раскрыть список объектов, содержащихся в объекте dut иерархии объектов Scope и выбрать там объект max_min_unit.

../.pic/Vivado%20Basics/Debug%20manual/fig_17.png

Рисунок 17. Добавление сигналов вложенных модулей на временную диаграмму.

Добавляем внутренние сигналы на временную диаграмму, группируем их под именем max_min, и повторяем моделирование.

../.pic/Vivado%20Basics/Debug%20manual/fig_18.png

Рисунок 18. Результат добавления и группировки сигналов подмодуля max_min.

Произошло что-то странное: все внутренние сигналы объекта max_min_unit "зеленые" (не имеющие X или Z состояния), однако подключенный к выходу этого модуля сигнал min находится в Z-состоянии. Как такое могло произойти?

Если присмотреться к сигналу min, находящемуся в Z-состоянии, можно заметить, что младшая цифра находится не в Z-состоянии, а в состоянии 0, такое же значение стоит и на сигнале min объекта max_min_unit. Это интересно.

Если присмотреться к этим двум сигналам еще пристальней, то можно увидеть, что у сигнала min объекта dut разрядность 32 бита, в то время как разрядность сигнала min объекта max_min_unit составляет 4 бита.

Это и является проблемой: мы подключили 4 бита сигнала 4-разрядного сигнала min к младшим 4 битам 32-разрядного сигнала min, а остальные разряды остались не подключенными.

По всей видимости, при написании модуля max_min, была указана неверная разрядность сигнала min, вместо 31 было написано 3. Исправим это и повторим моделирование.

Обратите внимание, что поскольку мы изменили исходный код, в этот раз необходимо нажать на кнопку Relaunch Simulation, поскольку нужна повторная компиляция проекта.

../.pic/Vivado%20Basics/Debug%20manual/fig_19.png

Рисунок 19. Результат моделирования после исправления разрядности сигнала min.

В логе сообщается о 102 найденных ошибках. Ровно на одну ошибку меньше, чем было ранее. Это не означает, что в проекте осталось 102 ошибки, только то, что, исправив данную ошибку — мы действительно что-то исправили, и один из тестовых сценариев, который ранее завершался ошибкой, теперь завершился без нее.

Помните, что если в проекте много ошибок, то часть ошибок может выправлять поведение других ошибок (хоть и не всегда, но иногда минус на минус может выдать плюс контексте ошибок проекта), поэтому надо осторожно полагаться на число найденных ошибок, если их больше нуля.

Посмотрим на нашу временную диаграмму снова, и выберем дальнейшие действия:

../.pic/Vivado%20Basics/Debug%20manual/fig_20.png

Рисунок 20. Временная диаграмма после исправления разрядности сигнала min.

Мы видим, что на временной диаграмме не осталось сигналов в X или Z-состоянии, а значит мы собрали все "низковисящие" улики нашего с вами расследования. Вернемся к месту преступления и попробуем поискать новые улики:

../.pic/Vivado%20Basics/Debug%20manual/fig_21.png

Рисунок 21. Первая ошибка в новом логе моделирования.

Поиск ошибки в сигналах, формирующих проблемный сигнал

Мы видим, что первой ошибкой в логе стала не та ошибка, что была прежде. Раньше первый неверный результат мы видели в момент времени 5ns, когда на дизайн подавались значения 0 и 0, теперь же первой ошибкой стал момент времени 10ns, когда на дизайн подаются значения 1 и 1. Наше устройство считает, что результат должен равняться 3, в то время как модель считает, что результат должен равняться 1. Проверим, нет ли ошибки в модели и посчитаем результат самостоятельно:

Для определения приблизительной длины вектора в евклидовом пространстве(вычисления квадратного корня из суммы квадратов / длины гипотенузы прямоугольного треугольника) можно воспользоваться формулой:

sqrt(a^2 + b^2) ≈ max + min/2, где max и min — большее и меньшее из пары чисел соответственно [Ричард Лайонс: Цифровая обработка сигналов, Глава 13.2, стр. 475].

Подставим наши числа в формулу (поскольку оба числа равны, не важно какое из них будет максимумом, а какое минимумом):

1 + 1/2 = 1.5

Ни модель, ни дизайн не правы?

На самом деле, наше устройство поддерживает только целочисленную арифметику, поэтому результат будет:

1 + 1/2 = 1 + 0 = 1

Модель правильно отразила особенность нашего устройства и дала корректный результат.

Значит надо смотреть как формируется результат в нашем устройстве, посмотрим на выход abs в модуле vector_abs:

assign abs = max + min_half;

Выход abs зависит от двух внутренних сигналов: max и min_half. В соответствии с нашим алгоритмом, либо проблема в логике, связывающей эти два сигнала (операции сложения), либо в значении какого-то из этих сигналов, либо комбинации этих вариантов.

Изучив модуль, мы понимаем, что в логике этого присваивания проблем нет, т.к. оно повторяет логику формулы max + min/2, складывая максимум с половиной минимума. Значит проблема в значении какого-то из этих сигналов (или обоих из них). Посчитаем значения этих сигналов самостоятельно (для сложного проекта эти значения бы посчитала модель):
1 и 0.

Смотрим, какие значения установлены на сигналах max и min_half в момент времени 10ns.

../.pic/Vivado%20Basics/Debug%20manual/fig_22.png

_Рисунок 22. Значения сигналов max и min_half в момент времени 10 ns

Обратите внимание: вы можете менять и цвета сигналов временной диаграммы через контекстное меню выделенных сигналов.

Мы видим, что в момент времени 10 ns значения max и min_half изменились ак 1 -> 4 и 2 -> 8 соответственно. Нас интересуют значения 1 и 2, т.к. в момент времени 10ns на выходе дизайна в этот момент был установившийся результат для предыдущих значений (еще не успел посчитаться результат для новых значений).

Значение max=1 совпадает с ожидаемым, в то время как min_half=2 явно нет.

Мы нашли причину неправильного вычисления результата: и правда, 1+2=3, теперь необходимо найти ошибку в вычислении сигнала min_half.

Как и с сигналом abs, необходимо определить сигналы, влияющие на значение сигнала min_half. Данный сигнал подключен к выходу quotient модуля half_divider, поэтому мы будем смотреть исходный код данного модуля:

module half_divider(
  input [31:0] numerator,
  output[31:0] quotient
);

  assign quotient = numerator << 1'b1;

endmodule

Что делает данный модуль? Он принимает на вход значение и делит его на два. На вход данного модуля будет приходить значение минимума из нашей формулы.

Выход данного модуля зависит от входа numerator и логики сдвига влево на 1. Это значит, что проблема либо в логике, либо в значении, подаваемом на вход. Выведем сигнал numerator на временную диаграмму и посмотрим на его значение в момент времени `10ns.

../.pic/Vivado%20Basics/Debug%20manual/fig_23.png

Рисунок 23. Значение сигнала numerator в момент времени 10 ns.

Мы помним, что в момент, когда дизайн начал выдавать неправильный результат, на его входы подавались числа 1 и 1, это значит, что на вход numerator пришло корректное значение: минимум из этих двух чисел и правда равен 1. Проверим логику данного модуля.

Исправление логики проблемного сигнала

Операция деления в цифровой схемотехнике является очень "дорогой" в плане ресурсов логических блоков и критического пути, поэтому этой операции часто стараются избегать. В нашем случае, нам не нужно обычное деление — нам нужно деление только напополам. В двоичной арифметике, для того чтобы разделить число на два, достаточно отбросить его младшую цифру. Вы часто пользуетесь подобной операцией в повседневной жизни при выполнении операции деления на 10: отбрасываете младшую цифру в десятичной арифметике.

Именно поэтому, когда мы в первый раз пытались посчитать результат "на бумаге", у нас было расхождение с моделью: когда мы делим 1 на 2, мы получаем 0.5, однако деление путем отбрасывания цифры округляет результат вниз (1/2=0, 15/10=1).

Как "отбросить" цифру средствами цифровой логики? Для этого используется операция сдвига вправо.

Операция сдвига вправо в SystemVerilog записывается оператором >>. Справа от оператора указывается число "отбрасываемых цифр", в нашем случае одна. Но постойте, в логике присваивания стоит оператор <<. Это ошибка, исправим ее!

Повторяем моделирование.

../.pic/Vivado%20Basics/Debug%20manual/fig_24.png

Рисунок 24. Результат моделирования после исправления оператора сдвига.

Снова на одну ошибку меньше. Не унываем, вряд ли в проекте число ошибок больше, чем число непустых строк самого проекта. Возвращаемся к начальной ошибке:

../.pic/Vivado%20Basics/Debug%20manual/fig_25.png

Рисунок 25. Первая ошибка в повторном моделировании.

Мы продвинулись во времени безошибочного моделирования до 15 ns, начинаем наше расследование с начала:

На вход дизайна подаются значения 3 и 4, дизайн считает, что результатом вычисления max + min/2 будет 2, модель считает, что 5. Посчитаем сами:

max=4
min=3
max + min/2 = 4 + 3/2 = 4 + 1 = 5

И снова модель выдала правильный результат. Разберемся в значениях сигналов, формирующих сигнал abs.

Проблема необъявленных сигналов

К этому моменту на вашей временной диаграмме скорей всего стало уже очень много сигналов. Уберем лишние, оставив только внутренние сигналы модуля vector_abs (для этого выделяем не нужные сигналы, и удаляем их с помощью клавиши Delete).

../.pic/Vivado%20Basics/Debug%20manual/fig_26.png

Рисунок 26. Поведение внутренних сигналов модуля vector_abs на временной диаграмме.

В глаза сразу же бросается, что сигнал max внешне отличается от всех остальных — он ведет себя как однобитный сигнал. Если все остальные сигналы 32-разрядные, то и сигнал max должен быть таким же. Перейдем к объявлению этого сигнала, чтобы это исправить (нажав правой кнопкой мыши, и выбрав Go To Source Code):

 module vector_abs(
   input [31:0] x,
   input [31:0] y,
   output[31:0] abs
 );


 wire [31:0] min;
 wire [31:0] min_half;

 max_min max_min_unit(
   .a(x),
   .b(y),
   .max(max),
   .min(min)
 );
//...

Это странно, курсор был установлен на строку .max(max), хотя раньше в этом случае курсор устанавливался на строку, где объявлялся выбранный сигнал. Но вот в чем дело, если мы просмотрим файл внимательно, то не обнаружим объявления сигнала вовсе. Как так вышло, что мы использовали необъявленный сигнал, а САПР не выдал нам ошибку? Дело в том, что стандарт IEEE 1364-2005 для языка SystemVerilog допускает подобное использование необъявленного сигнала. В этом случае, синтезатор неявно создаст одноименный одноразрядный сигнал, что и произошло.

Для исправления этой ошибки, объявим сигнал max с корректной разрядностью и повторим моделирование.

../.pic/Vivado%20Basics/Debug%20manual/fig_27.png

Рисунок 27. Результат моделирования после объявления пропущенного сигнала.

Самостоятельная работа

Число ошибок сократилось до 40! Мы явно на верном пути. Повторяем предыдущие шаги, вернувшись к первой ошибке:

../.pic/Vivado%20Basics/Debug%20manual/fig_28.png

Рисунок 28. Первая ошибка в повторном моделировании.

В этот раз первая ошибка осталась прежней, только теперь дизайн считает, что результат должен равняться шести (в прошлый раз дизайн выдавал 2). Мы уже убедились, что в этом случае модель дает правильный результат, поэтому сразу перейдем к формирующим результат сигналам:

../.pic/Vivado%20Basics/Debug%20manual/fig_29.png

Рисунок 29. Поведение внутренних сигналов модуля vector_abs на временной диаграмме.

Видим, что значение сигнала min_half, формирующего значение выхода abs неверно (минимумом из 3 и 4 является 3, 3/2 = 1).

Не отходя далеко от кассы, мы замечаем, что значение min, формирующее сигнал min_half неверно: его значение 4, а должно быть 3.

Используя файлы исходного кода проекта, попробуйте разобраться в последней обнаруженной нами ошибке.